/*
安装此模块，将
    在/proc/devices文件里生成设备(lxz_dev)及其主设备号
    在/sys/class/生成lxz_class目录
    在/sys/class/lxz_class/下生成lxz_device设备节点(文件夹)
    在/dev/下生成lxz_device设备节点，可read\write\ioctl\poll

    read在未设置fctl标志的非阻塞IO情况下，默认为阻塞调用

    本例
    1.使用等待队列实现非阻塞IO
    2.支持非阻塞IO
    3.支持poll
    4.使用mutex实现并发同步
    5.使用内核定时器作为消费者\生产者，消费buffer\生产buffer
*/

#include "linux/init.h"
#include "linux/module.h"
#include "linux/cdev.h"
#include "linux/device.h"
#include "linux/fs.h"
#include "linux/string.h"
#include "linux/uaccess.h"
#include "linux/wait.h"
#include "linux/atomic.h"
#include "linux/mutex.h"
#include "linux/slab.h"
#include "linux/timer.h"
#include "linux/jiffies.h"
#include "linux/poll.h"

#define MODULE_BUFFER_SIZE 1024

/*
lxz_chrdevice基于cdev/device，也可以说继承自cdev/device
*/
struct lxz_chrdevice
{
    dev_t chrdev_region_id;
    char *chrdev_region_name;       // 设备号对应名称/proc/devices中显示
    unsigned int first_minimor;     // 第一个次设备号
    unsigned int mininor_dev_count; // 次设备个数

    struct cdev cdev; // 内核字符设备实现

    char *sysfs_class_name;
    struct class *sysfs_class;
    struct device *sysfs_device; // sysfs通用设备
    char *sysfs_device_name;

    char read_buffer[MODULE_BUFFER_SIZE]; // 读缓存，用户程序是消费者
    atomic_t read_buffer_count;           // 读缓存的字节数

    char write_buffer[MODULE_BUFFER_SIZE]; // 写缓存，用户程序是生产者
    atomic_t write_buffer_count;           // 写缓存的字节数

    unsigned int ioctl_num; // ioctl接口的操作数据

    wait_queue_head_t read_wait_queue;  // 等待队列(头)，用于读
    wait_queue_head_t write_wait_queue; // 等待队列(头)，用于写

    struct file_operations fops;

    atomic_t open_count; // 本设备被打开的次数

    struct timer_list productor_timer; // 生产者定时器，产生数据到read_buffer
    unsigned long productor_interval;  // 生产者定时器间隔，单位ms

    struct timer_list consumer_timer; // 消费者定时器，消费write_buffer里的数据
    unsigned long consumer_interval;  // 消费者定时器间隔，单位ms

    struct mutex mutex;
};
static struct lxz_chrdevice *dev_ptr = NULL;

static void lxz_chrdevice_init(struct lxz_chrdevice **dev)
{
    struct lxz_chrdevice *result = kmalloc(sizeof(struct lxz_chrdevice), GFP_KERNEL);
    if (!result)
    {
        printk("kmalloc error,what\n");
        *dev = NULL;
        return;
    }

    result->chrdev_region_id = 0;
    result->chrdev_region_name = "lxz_chrdev_region";
    result->first_minimor = 0;
    result->mininor_dev_count = 1;

    (void)memset(&(result->cdev), 0, sizeof(result->cdev));

    result->sysfs_class_name = "lxz_class";
    result->sysfs_class = NULL;
    result->sysfs_device = NULL;
    result->sysfs_device_name = "lxz_device";

    (void)memset(result->read_buffer, 0, sizeof(result->read_buffer));
    atomic_set(&(result->read_buffer_count), 0);

    (void)memset(result->write_buffer, 0, sizeof(result->write_buffer));
    atomic_set(&(result->write_buffer_count), 0);

    result->ioctl_num = 11;

    init_waitqueue_head(&(result->read_wait_queue));
    init_waitqueue_head(&(result->write_wait_queue));

    (void)memset(&(result->fops), 0, sizeof(result->fops));

    atomic_set(&(result->open_count), 0);

    result->productor_interval = 10 * 1000;

    result->consumer_interval = 10 * 1000;

    mutex_init(&(result->mutex));

    *dev = result;
}

static void lxz_chrdevice_deinit(struct lxz_chrdevice **dev)
{
    mutex_destroy(&((*dev)->mutex));

    // wait_queue_head_t无销毁或反初始化函数

    kfree(*dev);
    *dev = NULL;
}

// 判断是否可读
static unsigned int lxz_chrdevice_readable(struct lxz_chrdevice *dev)
{
    return (atomic_read(&(dev->read_buffer_count)) > 0);
}

// 判断是否可写
static unsigned int lxz_chrdevice_writable(struct lxz_chrdevice *dev)
{
    return (atomic_read(&(dev->write_buffer_count)) == 0);
}

// 内核生产者定时回调
static void productor_callback(struct timer_list *timer)
{
    struct lxz_chrdevice *lxzchardevice = NULL;
    char product[128] = {0}; // 生产品
    size_t to_append = 0;    // 要追加的字节数
    int buffered_bytes = 0;
    unsigned long timeout = 0;

    // printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    (void)snprintf(product, sizeof(product), "jiffies=%lu;", jiffies);
    to_append = strlen(product);

    // 通过定时器指针获取lxz_chrdevice指针
    lxzchardevice = container_of(timer, struct lxz_chrdevice, productor_timer);

    mutex_lock(&(lxzchardevice->mutex));

    // 获取read_buffer缓存的字节数
    buffered_bytes = atomic_read(&(lxzchardevice->read_buffer_count));
    if ((buffered_bytes + to_append) > sizeof(lxzchardevice->read_buffer))
    {
        printk("lxzchardevice->read_buffer full,buffered_bytes=%d,to_append=%lu\n", buffered_bytes, to_append);
    }
    else
    {
        // 生产数据到read_buffer

        (void)memcpy(lxzchardevice->read_buffer + buffered_bytes, product, to_append);
        atomic_add(to_append, &(lxzchardevice->read_buffer_count));
    }

    // 唤醒读等待队列里的进程
    wake_up_all(&(lxzchardevice->read_wait_queue));

    // 更新定时器的到期时间
    timeout = jiffies + msecs_to_jiffies(lxzchardevice->productor_interval);
    mod_timer(timer, timeout);

    mutex_unlock(&(lxzchardevice->mutex));
}

// 内核消费者定时回调
static void consumer_callback(struct timer_list *timer)
{
    struct lxz_chrdevice *lxzchardevice = NULL;
    int buffered_bytes = 0;
    unsigned long timeout = 0;

    // printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    // 通过定时器指针获取lxz_chrdevice指针
    lxzchardevice = container_of(timer, struct lxz_chrdevice, consumer_timer);

    mutex_lock(&(lxzchardevice->mutex));

    // 获取write_buffer已缓存的字节数
    buffered_bytes = atomic_read(&(lxzchardevice->write_buffer_count));
    if (buffered_bytes)
    {
        // 消费write_buffer里的数据

        printk("lxzchardevice->write_buffer=%s\n", lxzchardevice->write_buffer);

        (void)memset(lxzchardevice->write_buffer, 0, sizeof(lxzchardevice->write_buffer));
        atomic_set(&(lxzchardevice->write_buffer_count), 0);
    }
    else
    {
        printk("write_buffer empty\n");
    }

    // 唤醒写等待队列中的进程
    wake_up_all(&(lxzchardevice->write_wait_queue));

    // 更新定时器的到期时间
    timeout = jiffies + msecs_to_jiffies(lxzchardevice->consumer_interval);
    mod_timer(timer, timeout);

    mutex_unlock(&(lxzchardevice->mutex));
}

int chrdev_open(struct inode *node, struct file *f)
{
    // printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    atomic_add(1, &(dev_ptr->open_count));
    printk("open_count=%d\n", atomic_read(&(dev_ptr->open_count)));

    return 0;
}

int chrdev_release(struct inode *node, struct file *f)
{
    // printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    atomic_sub(1, &(dev_ptr->open_count));
    printk("open_count=%d\n", atomic_read(&(dev_ptr->open_count)));

    return 0;
}

// 阻塞方式读(必须先等待后加锁，不能反过来，可以推演一下两个阻塞io程序的行为)
ssize_t chrdev_read_block(struct file *f, char __user *buffer, size_t n, loff_t *pos)
{
    size_t buffered_bytes = 0; // read_buffer内部缓存的字节数
    size_t to_copy = 0;        // 实际操作，读取的字节数
    unsigned long remain = 0;

    // printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    // 等待可读，系统调用将进入读等待(用户程序将陷入阻塞)
    wait_event_interruptible(dev_ptr->read_wait_queue, lxz_chrdevice_readable(dev_ptr));

    mutex_lock(&(dev_ptr->mutex));

    // 读取缓存的字节数
    buffered_bytes = atomic_read(&(dev_ptr->read_buffer_count));
    printk("wait_event_interruptible passed,buffered_bytes=%lu\n", buffered_bytes);

    // 计算实际操作，读取的字节数
    to_copy = n < buffered_bytes ? n : buffered_bytes;
    printk("n=%lu,to_copy=%lu\n", n, to_copy);

    // 拷贝到用户空间
    remain = copy_to_user(buffer, dev_ptr->read_buffer, to_copy);
    if (remain)
    {
        printk("remain(%lu),to_copy(%lu),what a bug\n", remain, to_copy);

        mutex_unlock(&(dev_ptr->mutex));
        return -EFAULT;
    }

    // 若有未拷贝完的数据，把剩下的数据放到buffer头部
    if (buffered_bytes - to_copy)
    {
        memmove(dev_ptr->read_buffer, dev_ptr->read_buffer + to_copy, buffered_bytes - to_copy);
    }

    // 设置read_buffer_count原子变量的值
    atomic_set(&(dev_ptr->read_buffer_count), buffered_bytes - to_copy);

    mutex_unlock(&(dev_ptr->mutex));

    return to_copy;
}

ssize_t chrdev_read_nonblock(struct file *f, char __user *buffer, size_t n, loff_t *pos)
{
    size_t buffered_bytes = 0; // buffer内部缓存的字节数
    size_t to_copy = 0;        // 实际操作，读取的字节数
    unsigned long remain = 0;

    // printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    mutex_lock(&(dev_ptr->mutex));

    // 判断是否可读
    if (lxz_chrdevice_readable(dev_ptr) == 0)
    {
        printk("lxz_chrdevice_readable false\n");

        mutex_unlock(&(dev_ptr->mutex));
        return -EAGAIN;
    }

    // 获取缓存的字节数
    buffered_bytes = atomic_read(&(dev_ptr->read_buffer_count));

    // 计算实际操作，读取的字节数
    to_copy = n < buffered_bytes ? n : buffered_bytes;
    printk("n=%lu,to_copy=%lu\n", n, to_copy);

    // 拷贝到用户空间
    remain = copy_to_user(buffer, dev_ptr->read_buffer, to_copy);
    if (remain)
    {
        printk("remain(%lu),to_copy(%lu),what a bug\n", remain, to_copy);

        mutex_unlock(&(dev_ptr->mutex));
        return -EFAULT;
    }

    // 若有未拷贝完的数据，把剩下的数据放到buffer头部
    if (buffered_bytes - to_copy)
    {
        memmove(dev_ptr->read_buffer, dev_ptr->read_buffer + to_copy, buffered_bytes - to_copy);
    }

    // 设置缓存的字节数
    atomic_set(&(dev_ptr->read_buffer_count), (buffered_bytes - to_copy));

    mutex_unlock(&(dev_ptr->mutex));

    return to_copy;
}

ssize_t chrdev_read(struct file *f, char __user *buffer, size_t n, loff_t *pos)
{
    // 非阻塞方式读
    if (f->f_flags & O_NONBLOCK)
    {
        return chrdev_read_nonblock(f, buffer, n, pos);
    }

    // 阻塞方式读
    return chrdev_read_block(f, buffer, n, pos);
}

// 阻塞方式写(必须先等待后加锁，不能反过来，可以推演一下两个阻塞io程序的行为)
ssize_t chrdev_write_block(struct file *f, const char __user *buffer, size_t n, loff_t *pos)
{
    size_t max_size = 0; // 缓存可写入的字节数
    size_t to_copy = 0;  // 实际操作，写入的字节数
    unsigned long remain = 0;

    // printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    // 等待可写，系统调用将进入写等待，用户程序将陷入阻塞
    wait_event_interruptible(dev_ptr->write_wait_queue, lxz_chrdevice_writable(dev_ptr));

    mutex_lock(&(dev_ptr->mutex));

    max_size = MODULE_BUFFER_SIZE;
    to_copy = n < max_size ? n : max_size;
    printk("n=%lu,to_copy=%lu\n", n, to_copy);

    // 从用户态拷贝数据
    remain = copy_from_user(dev_ptr->write_buffer, buffer, to_copy);
    if (remain)
    {
        printk("remain(%lu),to_copy(%lu),what a bug\n", remain, to_copy);

        mutex_unlock(&(dev_ptr->mutex));
        return -EFAULT;
    }
    printk("dev_ptr->write_buffer=%s\n", dev_ptr->write_buffer);

    // 设置原子变量的值
    atomic_set(&(dev_ptr->write_buffer_count), to_copy);

    mutex_unlock(&(dev_ptr->mutex));

    return to_copy;
}

ssize_t chrdev_write_nonblock(struct file *f, const char __user *buffer, size_t n, loff_t *pos)
{
    size_t max_size = 0; // 缓存可写入的字节数
    size_t to_copy = 0;
    unsigned long remain = 0;

    // printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    mutex_lock(&(dev_ptr->mutex));

    // 判断是否可写
    if (lxz_chrdevice_writable(dev_ptr) == 0)
    {
        printk("lxz_chrdevice_writable false\n");

        mutex_unlock(&(dev_ptr->mutex));
        return -EAGAIN;
    }

    max_size = sizeof(dev_ptr->write_buffer);

    // 计算写操作实际要写的字节数
    to_copy = n < max_size ? n : max_size;

    // 拷贝到用户空间
    remain = copy_from_user(dev_ptr->write_buffer, buffer, to_copy);
    if (remain)
    {
        printk("copy_from_user error\n");

        mutex_unlock(&(dev_ptr->mutex));
        return -EFAULT;
    }

    // 设置缓存的字节数
    atomic_set(&(dev_ptr->write_buffer_count), to_copy);

    mutex_unlock(&(dev_ptr->mutex));

    return to_copy;
}

ssize_t chrdev_write(struct file *f, const char __user *buffer, size_t n, loff_t *pos)
{
    // 非阻塞方式写
    if (f->f_flags & O_NONBLOCK)
    {
        return chrdev_write_nonblock(f, buffer, n, pos);
    }

    // 阻塞方式写
    return chrdev_write_block(f, buffer, n, pos);
}

#define LXZ_MAGIC 0xff
#define IOCTL_GETNUM _IOR(LXZ_MAGIC, 0, unsigned int)
#define IOCTL_PUTNUM _IOW(LXZ_MAGIC, 1, unsigned int)
long chrdev_ioctl(struct file *f, unsigned int ioctl_cmd, unsigned long arg)
{
    int ret = -1;
    unsigned int __user *ptr = (unsigned int *)arg; // 用户态地址

    mutex_lock(&(dev_ptr->mutex));

    if (ioctl_cmd == IOCTL_GETNUM)
    {
        printk("IOCTL_GETNUM\n");

        ret = put_user(dev_ptr->ioctl_num, ptr);
        if (ret != 0)
        {
            printk("put_user error\n");

            mutex_unlock(&(dev_ptr->mutex));
            return -1;
        }
        printk("put_user succeed,dev_ptr->ioctl_num=%u\n", dev_ptr->ioctl_num);

        mutex_unlock(&(dev_ptr->mutex));
        return 0;
    }
    else if (ioctl_cmd == IOCTL_PUTNUM)
    {
        printk("IOCTL_PUTNUM\n");

        ret = get_user(dev_ptr->ioctl_num, ptr);
        if (ret != 0)
        {
            printk("get_user error\n");

            mutex_unlock(&(dev_ptr->mutex));
            return -1;
        }
        printk("get_user succeed,dev_ptr->ioctl_num=%u\n", dev_ptr->ioctl_num);

        mutex_unlock(&(dev_ptr->mutex));
        return 0;
    }

    mutex_unlock(&(dev_ptr->mutex));
    return -1;
}

unsigned int chrdev_poll(struct file *f, struct poll_table_struct *pt)
{
    unsigned int events = 0;

    /*
    poll_wait只是将当前进程的等待队列项添加到驱动的等待队列中，并不会立即阻塞进程。
    但是加锁必须放在poll_wait后面
    */
    poll_wait(f, &(dev_ptr->read_wait_queue), pt);
    poll_wait(f, &(dev_ptr->write_wait_queue), pt);

    mutex_lock(&(dev_ptr->mutex));

    if (lxz_chrdevice_readable(dev_ptr))
    {
        events |= POLLIN;
    }

    if (lxz_chrdevice_writable(dev_ptr))
    {
        events |= POLLOUT;
    }

    mutex_unlock(&(dev_ptr->mutex));

    return events;
}

// 模块初始化
static int __init chrdev_init(void)
{
    int ret = -1;
    unsigned long timeout = 0;

    // printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    // 创建并初始化lxz_chrdevice结构
    lxz_chrdevice_init(&dev_ptr);

    // 分配设备号
    ret = alloc_chrdev_region(&(dev_ptr->chrdev_region_id), dev_ptr->first_minimor, dev_ptr->mininor_dev_count, dev_ptr->chrdev_region_name);
    if (ret != 0)
    {
        printk("alloc_chrdev_region error\n");

        lxz_chrdevice_deinit(&dev_ptr);
        return -1;
    }

    // 设置字符设备操作集合
    dev_ptr->fops.read = chrdev_read;
    dev_ptr->fops.write = chrdev_write;
    dev_ptr->fops.open = chrdev_open;
    dev_ptr->fops.release = chrdev_release;
    dev_ptr->fops.unlocked_ioctl = chrdev_ioctl;
    dev_ptr->fops.poll = chrdev_poll;

    // 初始化内核字符设备(绑定操作函数集合)
    cdev_init(&(dev_ptr->cdev), &(dev_ptr->fops));

    // 往内核添加若干个字符设备
    ret = cdev_add(&(dev_ptr->cdev), dev_ptr->chrdev_region_id, dev_ptr->mininor_dev_count);
    if (ret < 0)
    {
        printk("cdev_add error\n");

        unregister_chrdev_region(dev_ptr->chrdev_region_id, dev_ptr->mininor_dev_count);
        lxz_chrdevice_deinit(&dev_ptr);
        return -1;
    }

    // 创建sysfs设备类别
    dev_ptr->sysfs_class = class_create(THIS_MODULE, dev_ptr->sysfs_class_name);
    if (IS_ERR(dev_ptr->sysfs_class))
    {
        printk("class_create error\n");

        cdev_del(&(dev_ptr->cdev));
        unregister_chrdev_region(dev_ptr->chrdev_region_id, dev_ptr->mininor_dev_count);
        lxz_chrdevice_deinit(&dev_ptr);
        return -1;
    }
    printk("class_create succeed\n");

    // 创建基于设备号和sysfs设备类别的设备，将导出到/sys/class/
    dev_ptr->sysfs_device = device_create(dev_ptr->sysfs_class, NULL, dev_ptr->chrdev_region_id, NULL, dev_ptr->sysfs_device_name);
    if (IS_ERR(dev_ptr->sysfs_device))
    {
        printk("device_create error\n");

        class_destroy(dev_ptr->sysfs_class);
        cdev_del(&(dev_ptr->cdev));
        unregister_chrdev_region(dev_ptr->chrdev_region_id, dev_ptr->mininor_dev_count);
        lxz_chrdevice_deinit(&dev_ptr);
        return -1;
    }
    printk("device_create succeed\n");

    // 启动内核的定时器(消费和生产)
    timer_setup(&(dev_ptr->productor_timer), productor_callback, 0);
    timeout = jiffies + msecs_to_jiffies(dev_ptr->productor_interval);
    mod_timer(&(dev_ptr->productor_timer), timeout);

    timer_setup(&(dev_ptr->consumer_timer), consumer_callback, 0);
    timeout = jiffies + msecs_to_jiffies(dev_ptr->consumer_interval);
    mod_timer(&(dev_ptr->consumer_timer), timeout);

    return 0;
}

// 模块卸载
static void __exit chrdev_exit(void)
{
    // printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    // 先清理内核的定时器(消费和生产)
    del_timer(&(dev_ptr->consumer_timer));
    del_timer(&(dev_ptr->productor_timer));

    device_destroy(dev_ptr->sysfs_class, dev_ptr->chrdev_region_id);
    class_destroy(dev_ptr->sysfs_class);
    cdev_del(&(dev_ptr->cdev));
    unregister_chrdev_region(dev_ptr->chrdev_region_id, dev_ptr->mininor_dev_count);
    lxz_chrdevice_deinit(&dev_ptr);
}

module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lxz");
